#include "stdafx.h"
#include "report.h"

int g_id = 1;

bool contains_basic_block(List *basic_block_results, BasicBlock *bb) {
	Node *node;
	for(node = begin(basic_block_results); node != end(basic_block_results); node = node->next) {
		BasicBlockResult *r = (BasicBlockResult *)node->object;
		if(r->bb == bb)
			return true;
	}

	return false;
}

int get_id_basic_block(List *list, BasicBlock *bb) {
	Node *node;
	for(node = begin(list); node != end(list); node = node->next) {
		BasicBlockResult *r = (BasicBlockResult *)node->object;
		if(r->bb == bb)
			return r->id;
	}

	return 0;
}

BasicBlockResult *create_basic_block_result(BasicBlock *bb) {
	BasicBlockResult *r = (BasicBlockResult *)malloc(sizeof(BasicBlockResult));
	r->bb = bb;
	r->id = g_id++;

	return r;
}

EdgeResult *create_edge_result(int source, int target, const char *label) {
	EdgeResult *e = (EdgeResult *)malloc(sizeof(EdgeResult));
	strcpy(e->label, label);
	e->source = source;
	e->target = target;

	return e;
}

void print_file(FILE *fp, Report *r) {
	Node *node;

	for(node = begin(r->basic_block_results); node != end(r->basic_block_results); node = node->next) {
		BasicBlockResult *r = (BasicBlockResult *)node->object;
		fprintf(fp, "basic_block: { id: '%d', label: '%s' }\n", r->id, r->bb->source);
	}

	for(node = begin(r->edge_results); node != end(r->edge_results); node = node->next) {
		EdgeResult *e = (EdgeResult *)node->object;
		fprintf(fp, "edge: { source: '%d', target: '%d', label: '%s' }\n", e->source, e->target, e->label);
	}
}

void dfs(Report *r, BasicBlock *parent, BasicBlock *current, int index) {
	Node *node;
	EdgeResult *e = 0;
	int i = 0;

	if(parent) {
		bool is_contains = contains_basic_block(r->basic_block_results, current);
		if(is_contains && get_id_basic_block(r->basic_block_results, current) < get_id_basic_block(r->basic_block_results, parent)) {
			// back
			e = create_edge_result(get_id_basic_block(r->basic_block_results, parent), 
									get_id_basic_block(r->basic_block_results, current),
									"back");
		} else {
			bool is_normal = true;
			if(begin(parent->connect_bb_list)->next != end(parent->connect_bb_list)) {
				if(index == 0) {
					// true
					e = create_edge_result(get_id_basic_block(r->basic_block_results, parent),
											g_id,
											"true");
					is_normal = false;
				} else if(index == 1) {
					// false
					e = create_edge_result(get_id_basic_block(r->basic_block_results, parent),
											g_id,
											"false");
					is_normal = false;
				}
			}

			if(is_normal) {
				e = create_edge_result(get_id_basic_block(r->basic_block_results, parent),
										g_id,
										"");
			}
		}

		if(e) {
			add_list(r->edge_results, e);
			if(is_contains) {
				return ;
			}
		}
	}

	add_list(r->basic_block_results, create_basic_block_result(current));

	for(node = begin(current->connect_bb_list); node != end(current->connect_bb_list); node = node->next) {
		BasicBlock *bb = (BasicBlock *)node->object;
		
		dfs(r, current, bb, i++);
	}
}

Report *create_report() {
	Report *report_result = (Report *)malloc(sizeof(Report));
	report_result->basic_block_results = create_list();
	report_result->edge_results = create_list();

	return report_result;
}

void destroy_report(Report *r) {
	Node *node;

	for(node = begin(r->basic_block_results); node != end(r->basic_block_results); node = node->next) {
		free(node->object);
	}

	for(node = begin(r->edge_results); node != end(r->edge_results); node = node->next) {
		free(node->object);
	}

	destroy_list(r->basic_block_results);
	destroy_list(r->edge_results);

	free(r);
}

void report(CFG *cfg, const char *report_file) {
	Report *report_result = create_report();
	FILE *fp;

	dfs(report_result, 0, cfg->start, 0);

	fp = fopen(report_file, "wt");
	print_file(fp, report_result);
	fclose(fp);
	destroy_report(report_result);
}